home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0-b / stk-3 / blt-for-STk-3.0 / bltGrAxis.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-26  |  73.6 KB  |  2,390 lines

  1. /*
  2.  * bltGrAxis.c --
  3.  *
  4.  *    This module implements coordinate axes for a graph
  5.  *    widget in the Tk toolkit.
  6.  *
  7.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  8.  * Permission to use, copy, modify, and distribute this software
  9.  * and its documentation for any purpose and without fee is hereby
  10.  * granted, provided that the above copyright notice appear in all
  11.  * copies and that both that the copyright notice and warranty
  12.  * disclaimer appear in supporting documentation, and that the
  13.  * names of AT&T Bell Laboratories any of their entities not be used
  14.  * in advertising or publicity pertaining to distribution of the
  15.  * software without specific, written prior permission.
  16.  *
  17.  * AT&T disclaims all warranties with regard to this software, including
  18.  * all implied warranties of merchantability and fitness.  In no event
  19.  * shall AT&T be liable for any special, indirect or consequential
  20.  * damages or any damages whatsoever resulting from loss of use, data
  21.  * or profits, whether in an action of contract, negligence or other
  22.  * tortuous action, arising out of or in connection with the use or
  23.  * performance of this software.
  24.  *
  25.  */
  26.  
  27. #ifdef STk_CODE
  28.   /* Define NDEBUG for Gnu Libc, to avoid the inclusion of "assert" code which
  29.    * use "eprintf" which is not loaded in base STk. */
  30. #  define NDEBUG
  31. #endif
  32.  
  33. #include <assert.h>
  34. #include "blt.h"
  35. #include "bltGraph.h"
  36. #include "bltGrElem.h"
  37. #include <ctype.h>
  38. #include <X11/Xutil.h>
  39. #include <X11/Xatom.h>
  40.  
  41. #define NTICK 5
  42.  
  43. #ifndef SHRT_MIN
  44. #define SHRT_MIN                -0x8000
  45. #endif /* SHRT_MIN */
  46. #ifndef SHRT_MAX
  47. #define SHRT_MAX                 0x7FFF
  48. #endif /* SHRT_MAX */
  49.  
  50. /*
  51.  * Round x in terms of units
  52.  */
  53. #define UROUND(x,u)    (BLT_RND((x)/(u))*(u))
  54. #define UCEIL(x,u)    (ceil((x)/(u))*(u))
  55. #define UFLOOR(x,u)    (floor((x)/(u))*(u))
  56.  
  57. #define DEF_MAJOR_TICK     0.030    /* Length of a major tick */
  58. #define DEF_MINOR_TICK     0.015    /* Length of a minor (sub)tick */
  59. #define DEF_LABEL_TICK     0.040    /* Distance from graph to start of label */
  60.  
  61. #define NUMDIGITS    15    /* Specifies the number of digits of
  62.                  * accuracy used when outputting axis
  63.                  * tick labels. */
  64. enum PositionIndices {
  65.     MAJOR_TICK, MINOR_TICK, TICK_LABEL, AXIS_LINE
  66. };
  67.  
  68. #define AVG_TICK_NUM_CHARS    16    /* Assumed average tick label size */
  69. #define LABEL_MAX_SIZE        127    /* New buffer size to avoid crashes */
  70.  
  71. enum LimitIndices {
  72.     LMIN, LMAX
  73. };
  74.  
  75. /* Note: The following array is ordered according to enum AxisTypes */
  76. static char *axisNames[] =
  77. {
  78.     "x", "y", "x2", "y2"
  79. };
  80.  
  81. /* Map normalized coordinates to window coordinates */
  82. #define MAPX(X,x)        (BLT_RND((x)*(X)->scale)+(X)->offset)
  83. #define MAPY(Y,y)        ((Y)->offset-BLT_RND((y)*(Y)->scale))
  84.  
  85. /* Map graph coordinates to normalized coordinates [0..1] */
  86. #define NORM(a,x)     (((x) - (a)->min) / (a)->range)
  87.  
  88. /*
  89.  * Sun's bundled and unbundled C compilers choke on static function
  90.  * typedefs (while it can handle extern declarations) like
  91.  *
  92.  *     static Tk_OptionParseProc parseProc;
  93.  *      static Tk_OptionPrintProc printProc;
  94.  *
  95.  * As a workaround, provide forward declarations here:
  96.  */
  97. static int ParseAxisLimit _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec, int offset));
  98. static char *PrintAxisLimit _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  99.  
  100. static Tk_CustomOption MinLimitOption =
  101. {
  102.     ParseAxisLimit, PrintAxisLimit, (ClientData)LMIN,
  103. };
  104. static Tk_CustomOption MaxLimitOption =
  105. {
  106.     ParseAxisLimit, PrintAxisLimit, (ClientData)LMAX,
  107. };
  108.  
  109. /*
  110.  * ----------------------------------------------------------------------
  111.  *
  112.  * Label --
  113.  *
  114.  *     Structure containing the formatted label and the screen
  115.  *     coordinates position of the tick label (anchored at its
  116.  *     center).
  117.  *
  118.  * ----------------------------------------------------------------------
  119.  */
  120. typedef struct {
  121.     char *text;            /* Label for tick on axis */
  122.     short int x, y;        /* Window position of tick on graph */
  123. } Label;
  124.  
  125. /*
  126.  * ----------------------------------------------------------------------
  127.  *
  128.  * Axis --
  129.  *
  130.  *     Structure contains options controlling how the axis will be
  131.  *     displayed.
  132.  *
  133.  * ----------------------------------------------------------------------
  134.  */
  135.  
  136. typedef struct {
  137.     enum AxisTypes type;    /* Type of axis: X1, Y1, X2, or Y2 */
  138.     enum AxisLocations location;/* Location of the axis relative to plotting
  139.                  * surface: right, left, etc. */
  140.     int logScale;        /* If non-zero, scale values logarithmically */
  141.     int mapped;            /* If non-zero, display the axis */
  142.  
  143.     AxisDisplayProc *displayProc;
  144.     AxisPrintProc *printProc;
  145.     AxisLayoutProc *layoutProc;
  146.     AxisDestroyProc *destroyProc;
  147.  
  148.     /* User-definable fields */
  149.     char *title;        /* Axis title */
  150.     int showTicks;        /* If non-zero, display (both major
  151.                  * and minor) ticks on the axis */
  152.     int loose;            /* If non-zero, autoscale limits loosely */
  153.     int descending;        /* In non-zero, axis values are descending
  154.                  * instead of monotonically increasing. */
  155.     double limits[2];        /* Limits for scaling of axis */
  156.     double prevMin, prevMax;    /* Last limits for scaling of axis */
  157.     double reqStep;        /* Manually selected step size for
  158.                  * major ticks: If zero or less,
  159.                  * automatically calculate a "best"
  160.                  * step size based on range of
  161.                  * values. */
  162.     int reqSubTicks;        /* Manually selected # of subticks:
  163.                  * The default value is 2. */
  164.     XFontStruct *fontPtr;    /* Font used to draw tick labels. */
  165.     XColor *fgColorPtr;        /* Foreground color for ticks, labels,
  166.                  * and axis */
  167.     int lineWidth;        /* Line thickness of axis and ticks */
  168.     double theta;        /* Rotation of tick labels in degrees. */
  169.     char *formatCmd;        /* If non-NULL, indicates a Tcl proc
  170.                  * to call when formatting tick
  171.                  * labels. See the manual for its
  172.                  * usage. */
  173.     double subStep;        /* Step interval between minor ticks */
  174.     int subTicks;        /* # of minor ticks between major
  175.                  * ticks */
  176.     double step;        /* Step interval between major ticks */
  177.     int numTicks;        /* # of major ticks possible on axis:
  178.                  * Calculated by tick layout routines. */
  179.  
  180.     /* Calculated values */
  181.     unsigned int flags;
  182.  
  183.     Tk_Anchor anchor;        /* Anchor of tick labels */
  184.     int posArr[4];        /* Screen location of axis, major tick,
  185.                  * minor tick, and tick label */
  186.     XPoint titlePos;        /* Coordinates of axis title */
  187.  
  188.     Tcl_Interp *interp;
  189.     unsigned int width, height;    /* Bounding box of axis */
  190.  
  191.     double tickMin, tickMax;    /* Smallest and largest possible major
  192.                  * ticks on the plot */
  193.     int tickLength;        /* Legend of major tick on axis. */
  194.     double min, max;        /* Actual (including padding) axis limits */
  195.     double range;        /* Range of values (max-min) */
  196.     double scale;        /* Scale factor to convert values to
  197.                  * pixels */
  198.     int offset;            /* Offset of plotting region from window
  199.                  * origin */
  200.  
  201.     GC lineGC;            /* Graph context for axis lines and ticks */
  202.     GC textGC;            /* Graphic context for tick labels:
  203.                  * Must be a private GC (can't use
  204.                  * Tk_GetGC) because the FillStyle,
  205.                  * TSOrigin, and Stipple fields may
  206.                  * change when the graph is layout is
  207.                  * calculated, to accommodate rotation
  208.                  * of text. Also, note that the
  209.                  * background color is also reset when
  210.                  * the background color of the graph
  211.                  * changes. */
  212.     int numSegments;        /* Size of the above segment array */
  213.     XSegment *segArr;        /* Array of computed tick line
  214.                  * segments. Also includes the axis
  215.                  * line */
  216.     int numLabels;        /* Size of the above label array */
  217.     Label *labelArr;        /* Array of computed tick labels: See the
  218.                  * description of the Label structure. */
  219.  
  220. } Axis;
  221.  
  222. /* Axis flags: */
  223. #define AXIS_CONFIG_DIRTY    (1<<8)
  224. #define AXIS_CONFIG_USER_BIT    (1<<9)
  225. #define AXIS_CONFIG_MIN_MASK    (AXIS_CONFIG_USER_BIT << LMIN)
  226. #define AXIS_CONFIG_MAX_MASK    (AXIS_CONFIG_USER_BIT << LMAX)
  227. #define AXIS_CONFIG_MIN_SET(a)     ((a)->flags & AXIS_CONFIG_MIN_MASK)
  228. #define AXIS_CONFIG_MAX_SET(a)     ((a)->flags & AXIS_CONFIG_MAX_MASK)
  229.  
  230. #define VERTICAL_AXIS(a)    ((a)->location&1)
  231. #define HORIZONTAL_AXIS(a)        (!((a)->location&1))
  232.  
  233. #define DEF_AXIS_ALT_MAPPED     "false"
  234. #define DEF_AXIS_COMMAND    (char *)NULL
  235. #define DEF_AXIS_DESCENDING     "0"
  236. #define DEF_AXIS_FG_COLOR    BLACK
  237. #define DEF_AXIS_FG_MONO    BLACK
  238. #define DEF_AXIS_FONT         "-*-Courier-Medium-R-Normal--*-100-*-*-*-*-*-*"
  239. #define DEF_AXIS_TICK_LENGTH    "0.1i"
  240. #define DEF_AXIS_LINE_WIDTH    "0"
  241. #define DEF_AXIS_LOG_SCALE     "0"
  242. #define DEF_AXIS_LOOSE         "0"
  243. #define DEF_AXIS_MAX        (char *)NULL
  244. #define DEF_AXIS_MIN        (char *)NULL
  245. #define DEF_AXIS_ROTATE        "0.0"
  246. #define DEF_AXIS_STD_MAPPED     "1"
  247. #define DEF_AXIS_STEPSIZE    "0.0"
  248. #define DEF_AXIS_SUBTICKS    "2"
  249. #define DEF_AXIS_TICKS        "1"
  250. #define DEF_AXIS_X_STEPSIZE_BARCHART    "1.0"
  251. #define DEF_AXIS_X_SUBTICKS_BARCHART    "0"
  252. #define DEF_AXIS_TITLE        (char *)NULL
  253.  
  254. static Tk_ConfigSpec xAxisConfigSpecs[] =
  255. {
  256.     {TK_CONFIG_COLOR, "-color", "xColor", "AxisColor",
  257.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  258.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  259.     {TK_CONFIG_COLOR, "-color", "xColor", "AxisColor",
  260.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  261.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  262.     {TK_CONFIG_STRING, "-command", "xCommand", "AxisCommand",
  263.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  264.     ALL_MASK | TK_CONFIG_NULL_OK},
  265.     {TK_CONFIG_BOOLEAN, "-descending", "xDescending", "AxisDescending",
  266.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  267.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  268.     {TK_CONFIG_FONT, "-font", "xFont", "AxisFont",
  269.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  270.     {TK_CONFIG_PIXELS, "-linewidth", "xLinewidth", "AxisLinewidth",
  271.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  272.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  273.     {TK_CONFIG_BOOLEAN, "-logscale", "xLogscale", "AxisLogscale",
  274.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  275.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  276.     {TK_CONFIG_BOOLEAN, "-loose", "xLoose", "AxisLoose",
  277.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  278.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  279.     {TK_CONFIG_BOOLEAN, "-mapped", "xMapped", "AxisMapped",
  280.     DEF_AXIS_STD_MAPPED, Tk_Offset(Axis, mapped),
  281.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  282.     {TK_CONFIG_CUSTOM, "-max", "xMax", "AxisMax",
  283.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  284.     {TK_CONFIG_CUSTOM, "-min", "xMin", "AxisMin",
  285.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  286.     {TK_CONFIG_DOUBLE, "-rotate", "xRotate", "AxisRotate",
  287.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  288.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  289.     {TK_CONFIG_BOOLEAN, "-showticks", "xShowticks", "AxisShowticks",
  290.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  291.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  292.     {TK_CONFIG_DOUBLE, "-stepsize", "xStepsize", "AxisStepsize",
  293.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep), XYGRAPH_MASK},
  294.     {TK_CONFIG_DOUBLE, "-stepsize", "xStepsize", "AxisStepsize",
  295.     DEF_AXIS_X_STEPSIZE_BARCHART, Tk_Offset(Axis, reqStep),
  296.     BARCHART_MASK},
  297.     {TK_CONFIG_INT, "-subticks", "xSubticks", "AxisSubticks",
  298.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), XYGRAPH_MASK},
  299.     {TK_CONFIG_INT, "-subticks", "xSubticks", "AxisSubticks",
  300.     DEF_AXIS_X_SUBTICKS_BARCHART, Tk_Offset(Axis, reqSubTicks),
  301.     BARCHART_MASK},
  302.     {TK_CONFIG_PIXELS, "-ticklength", "xTickLength", "AxisTickLength",
  303.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  304.     {TK_CONFIG_STRING, "-title", "xTitle", "AxisTitle",
  305.     "X", Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  306.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  307. };
  308.  
  309.  
  310. static Tk_ConfigSpec x2AxisConfigSpecs[] =
  311. {
  312.     {TK_CONFIG_COLOR, "-color", "x2Color", "AxisColor",
  313.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  314.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  315.     {TK_CONFIG_COLOR, "-color", "x2Color", "AxisColor",
  316.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  317.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  318.     {TK_CONFIG_STRING, "-command", "x2Command", "AxisCommand",
  319.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  320.     ALL_MASK | TK_CONFIG_NULL_OK},
  321.     {TK_CONFIG_BOOLEAN, "-descending", "x2Descending", "AxisDescending",
  322.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  323.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  324.     {TK_CONFIG_FONT, "-font", "x2Font", "AxisFont",
  325.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  326.     {TK_CONFIG_PIXELS, "-linewidth", "x2Linewidth", "AxisLinewidth",
  327.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  328.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  329.     {TK_CONFIG_BOOLEAN, "-logscale", "x2Logscale", "AxisLogscale",
  330.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  331.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  332.     {TK_CONFIG_BOOLEAN, "-loose", "x2Loose", "AxisLoose",
  333.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  334.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  335.     {TK_CONFIG_BOOLEAN, "-mapped", "x2Mapped", "AxisMapped",
  336.     DEF_AXIS_ALT_MAPPED, Tk_Offset(Axis, mapped),
  337.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  338.     {TK_CONFIG_CUSTOM, "-max", "x2Max", "AxisMax",
  339.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  340.     {TK_CONFIG_CUSTOM, "-min", "x2Min", "AxisMin",
  341.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  342.     {TK_CONFIG_DOUBLE, "-rotate", "x2Rotate", "AxisRotate",
  343.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  344.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  345.     {TK_CONFIG_BOOLEAN, "-showticks", "x2Showticks", "AxisShowticks",
  346.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks), ALL_MASK},
  347.     {TK_CONFIG_DOUBLE, "-stepsize", "x2Stepsize", "AxisStepsize",
  348.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  349.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  350.     {TK_CONFIG_INT, "-subticks", "x2Subticks", "AxisSubticks",
  351.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks),
  352.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  353.     {TK_CONFIG_PIXELS, "-ticklength", "x2TickLength", "AxisTickLength",
  354.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  355.     {TK_CONFIG_STRING, "-title", "x2Title", "AxisTitle",
  356.     DEF_AXIS_TITLE, Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  357.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  358. };
  359.  
  360. static Tk_ConfigSpec yAxisConfigSpecs[] =
  361. {
  362.     {TK_CONFIG_COLOR, "-color", "yColor", "AxisColor",
  363.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  364.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  365.     {TK_CONFIG_COLOR, "-color", "yColor", "AxisColor",
  366.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  367.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  368.     {TK_CONFIG_STRING, "-command", "yCommand", "AxisCommand",
  369.     (char *)NULL, Tk_Offset(Axis, formatCmd),
  370.     ALL_MASK | TK_CONFIG_NULL_OK},
  371.     {TK_CONFIG_BOOLEAN, "-descending", "yDescending", "AxisDescending",
  372.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  373.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  374.     {TK_CONFIG_FONT, "-font", "yFont", "AxisFont",
  375.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  376.     {TK_CONFIG_BOOLEAN, "-loose", "yLoose", "AxisLoose",
  377.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  378.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  379.     {TK_CONFIG_PIXELS, "-linewidth", "yLinewidth", "AxisLinewidth",
  380.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  381.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  382.     {TK_CONFIG_BOOLEAN, "-logscale", "yLogscale", "AxisLogscale",
  383.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  384.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  385.     {TK_CONFIG_BOOLEAN, "-mapped", "yMapped", "AxisMapped",
  386.     DEF_AXIS_STD_MAPPED, Tk_Offset(Axis, mapped),
  387.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  388.     {TK_CONFIG_CUSTOM, "-max", "yMax", "AxisMax",
  389.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  390.     {TK_CONFIG_CUSTOM, "-min", "yMin", "AxisMin",
  391.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  392.     {TK_CONFIG_DOUBLE, "-rotate", "yRotate", "AxisRotate",
  393.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  394.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  395.     {TK_CONFIG_BOOLEAN, "-showticks", "yShowticks", "AxisShowticks",
  396.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  397.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  398.     {TK_CONFIG_DOUBLE, "-stepsize", "yStepsize", "AxisStepsize",
  399.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  400.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  401.     {TK_CONFIG_INT, "-subticks", "ySubticks", "AxisSubticks",
  402.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), ALL_MASK},
  403.     {TK_CONFIG_PIXELS, "-ticklength", "yTickLength", "AxisTickLength",
  404.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  405.     {TK_CONFIG_STRING, "-title", "yTitle", "AxisTitle",
  406.     "Y", Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  407.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  408. };
  409.  
  410. static Tk_ConfigSpec y2AxisConfigSpecs[] =
  411. {
  412.     {TK_CONFIG_COLOR, "-color", "y2Color", "AxisColor",
  413.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  414.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  415.     {TK_CONFIG_COLOR, "-color", "y2Color", "AxisColor",
  416.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  417.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  418.     {TK_CONFIG_STRING, "-command", "y2Command", "AxisCommand",
  419.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  420.     ALL_MASK | TK_CONFIG_NULL_OK},
  421.     {TK_CONFIG_BOOLEAN, "-descending", "y2Descending", "AxisDescending",
  422.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  423.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  424.     {TK_CONFIG_FONT, "-font", "y2Font", "AxisFont",
  425.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  426.     {TK_CONFIG_PIXELS, "-linewidth", "y2Linewidth", "AxisLinewidth",
  427.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  428.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  429.     {TK_CONFIG_BOOLEAN, "-loose", "y2Loose", "AxisLoose",
  430.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  431.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  432.     {TK_CONFIG_BOOLEAN, "-logscale", "y2Logscale", "AxisLogscale",
  433.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  434.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  435.     {TK_CONFIG_BOOLEAN, "-mapped", "y2Mapped", "AxisMapped",
  436.     DEF_AXIS_ALT_MAPPED, Tk_Offset(Axis, mapped),
  437.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  438.     {TK_CONFIG_CUSTOM, "-max", "y2Max", "AxisMax",
  439.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  440.     {TK_CONFIG_CUSTOM, "-min", "y2Min", "AxisMin",
  441.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  442.     {TK_CONFIG_DOUBLE, "-rotate", "y2Rotate", "AxisRotate",
  443.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  444.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  445.     {TK_CONFIG_BOOLEAN, "-showticks", "y2Showticks", "AxisShowticks",
  446.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  447.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  448.     {TK_CONFIG_DOUBLE, "-stepsize", "y2Stepsize", "AxisStepsize",
  449.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  450.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  451.     {TK_CONFIG_INT, "-subticks", "y2Subticks", "AxisSubticks",
  452.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), ALL_MASK},
  453.     {TK_CONFIG_PIXELS, "-ticklength", "y2TickLength", "AxisTickLength",
  454.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  455.     {TK_CONFIG_STRING, "-title", "y2Title", "AxisTitle",
  456.     DEF_AXIS_TITLE, Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  457.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  458. };
  459.  
  460. static Tk_ConfigSpec *axisConfigSpecs[4] =
  461. {
  462.     xAxisConfigSpecs, yAxisConfigSpecs,    /* Normal x-axis and y-axis */
  463.     x2AxisConfigSpecs, y2AxisConfigSpecs    /* Alternate x-axis and y-axis */
  464. };
  465.  
  466. /* ----------------------------------------------------------------------
  467.  * Custom option parse and print procedures
  468.  * ----------------------------------------------------------------------
  469.  */
  470. /*
  471.  * ----------------------------------------------------------------------
  472.  *
  473.  * ParseAxisLimit --
  474.  *
  475.  *    Convert the string representation of an axis limit into its
  476.  *    numeric form.
  477.  *
  478.  * Results:
  479.  *    The return value is a standard Tcl result.  The symbol type is
  480.  *    written into the widget record.
  481.  *
  482.  * ----------------------------------------------------------------------
  483.  */
  484. /*ARGSUSED*/
  485. static int
  486. ParseAxisLimit(clientData, interp, tkwin, value, widgRec, offset)
  487.     ClientData clientData;    /* Either LMIN or LMAX */
  488.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  489.     Tk_Window tkwin;        /* not used */
  490.     char *value;        /* */
  491.     char *widgRec;        /* Axis structure */
  492.     int offset;            /* Offset of limit */
  493. {
  494.     Axis *axisPtr = (Axis *)(widgRec);
  495.     int whichLimit = (int)clientData;
  496.     unsigned int mask;
  497.  
  498.     mask = (AXIS_CONFIG_USER_BIT << whichLimit);
  499.     if ((value == NULL) || (*value == '\0')) {
  500.     axisPtr->flags &= ~mask;
  501.     } else {
  502.     double newLimit;
  503.  
  504.     if (Tcl_ExprDouble(interp, value, &newLimit) != TCL_OK) {
  505.         return TCL_ERROR;
  506.     }
  507.     axisPtr->limits[whichLimit] = newLimit;
  508.     axisPtr->flags |= mask;
  509.     }
  510.     return TCL_OK;
  511. }
  512.  
  513. /*
  514.  * ----------------------------------------------------------------------
  515.  *
  516.  * PrintAxisLimit --
  517.  *
  518.  *    Convert the floating point axis limit into a string.
  519.  *
  520.  * Results:
  521.  *    The string representation of the limit is returned.
  522.  *
  523.  * ----------------------------------------------------------------------
  524.  */
  525. /*ARGSUSED*/
  526. static char *
  527. PrintAxisLimit(clientData, tkwin, widgRec, offset, freeProcPtr)
  528.     ClientData clientData;    /* Either LMIN or LMAX */
  529.     Tk_Window tkwin;        /* not used */
  530.     char *widgRec;        /* */
  531.     int offset;
  532.     Tcl_FreeProc **freeProcPtr;
  533. {
  534.     Axis *axisPtr = (Axis *)(widgRec);
  535.     int whichLimit = (int)clientData;
  536.     unsigned int mask;
  537.     char *result;
  538.  
  539.     result = "";
  540.     mask = (AXIS_CONFIG_USER_BIT << whichLimit);
  541.     if (axisPtr->flags & mask) {
  542.     char string[TCL_DOUBLE_SPACE + 1];
  543.  
  544.     Tcl_PrintDouble(axisPtr->interp, axisPtr->limits[whichLimit],
  545.         string);
  546.     result = strdup(string);
  547.     if (result == NULL) {
  548.         return "";
  549.     }
  550.     *freeProcPtr = (Tcl_FreeProc *)free;
  551.     }
  552.     return result;
  553. }
  554.  
  555. /*
  556.  * ----------------------------------------------------------------------
  557.  *
  558.  * MakeLabel --
  559.  *
  560.  *    Converts a floating point tick value to a string representation.
  561.  *
  562.  * Results:
  563.  *    Returns a formatted label in the string buffer.
  564.  *
  565.  * Side Effects:
  566.  *    Formatted tick label will be displayed on the graph.
  567.  *
  568.  * ----------------------------------------------------------------------
  569.  */
  570. static int
  571. MakeLabel(graphPtr, axisPtr, value, string)
  572.     Graph *graphPtr;        /* Graph widget */
  573.     Axis *axisPtr;        /* Axis structure */
  574.     double value;        /* */
  575.     char string[];        /* string (length is always
  576.                  * LABEL_MAX_SIZE+1) containing the
  577.                  * formatted label */
  578. {
  579.     if (axisPtr->logScale) {
  580.     sprintf(string, "1E%d", BLT_RND(value));
  581.     } else {
  582.     if (axisPtr->formatCmd == NULL) {
  583.         sprintf(string, "%.*g", NUMDIGITS, value);
  584.     } else {
  585.         Tcl_PrintDouble(axisPtr->interp, value, string);
  586.     }
  587.     }
  588.     if (axisPtr->formatCmd != NULL) {
  589.     Tcl_ResetResult(axisPtr->interp);
  590.     if (Tcl_VarEval(axisPtr->interp, axisPtr->formatCmd, " ",
  591.         Tk_PathName(graphPtr->tkwin), " ",
  592.         string, (char *)NULL) != TCL_OK) {
  593.         Tk_BackgroundError(axisPtr->interp);
  594.     } else {
  595.         char *result;
  596.  
  597.         result = axisPtr->interp->result;
  598.         if (*result != '\0') {
  599.         strncpy(string, result, LABEL_MAX_SIZE);
  600.         string[LABEL_MAX_SIZE] = 0;
  601.         Tcl_ResetResult(axisPtr->interp);
  602.         }
  603.     }
  604.     }
  605.     return TCL_OK;
  606. }
  607.  
  608. /* Map graph coordinate to normalized coordinates (consider log scale) */
  609. static double
  610. Scale(axisPtr, x)
  611.     Axis *axisPtr;
  612.     double x;
  613. {
  614.     if (x == Blt_posInfinity) {
  615.     return (1.0);
  616.     } else if (x == Blt_negInfinity) {
  617.     return (0.0);
  618.     }
  619.     if (axisPtr->logScale) {
  620.     if (x > 0.0) {
  621.         x = log10(x);
  622.     } else if (x < 0.0) {
  623.         x = 0.0;
  624.     }
  625.     }
  626.     return (NORM(axisPtr, x));
  627. }
  628.  
  629. /*
  630.  * ----------------------------------------------------------------------
  631.  *
  632.  * Blt_InvTransform --
  633.  *
  634.  *    Maps the given window y-coordinate back to a graph coordinate
  635.  *    value. Called by the graph locater routine.
  636.  *
  637.  * Results:
  638.  *    Returns the graph coordinate value at the given window
  639.  *    y-coordinate.
  640.  *
  641.  * ----------------------------------------------------------------------
  642.  */
  643. double
  644. Blt_InvTransform(axis, coord)
  645.     GraphAxis *axis;
  646.     int coord;
  647. {
  648.     double norm, value;
  649.     Axis *axisPtr = (Axis *)axis;
  650.  
  651.     if (HORIZONTAL_AXIS(axisPtr)) {
  652.     coord = (coord - axisPtr->offset);
  653.     } else {
  654.     coord = (axisPtr->offset - coord);
  655.     }
  656.     norm = ((double)coord / axisPtr->scale);
  657.     if (axisPtr->descending) {
  658.     norm = 1.0 - norm;
  659.     }
  660.     value = (norm * axisPtr->range) + axisPtr->min;
  661.     if (axisPtr->logScale) {
  662.     value = BLT_EXP10(value);
  663.     }
  664.     return (value);
  665. }
  666.  
  667. /*
  668.  * ----------------------------------------------------------------------
  669.  *
  670.  * Blt_Transform --
  671.  *
  672.  *    Map the given graph coordinate value to its axis, returning a
  673.  *    window position.
  674.  *
  675.  * Results:
  676.  *    Returns the window coordinate position on the given axis.
  677.  *
  678.  * Note:
  679.  *    Since line and polygon clipping is performed by the X server,
  680.  *    we must be careful about coordinates which are outside of the
  681.  *      range of a signed short int.
  682.  *
  683.  * ----------------------------------------------------------------------
  684.  */
  685. int
  686. Blt_Transform(axis, value)
  687.     GraphAxis *axis;
  688.     double value;
  689. {
  690.     Axis *axisPtr = (Axis *)axis;
  691.     double norm;
  692.     int coord;
  693.  
  694.     norm = Scale(axisPtr, value);
  695.     if (axisPtr->descending) {
  696.     norm = 1.0 - norm;
  697.     }
  698.     coord = HORIZONTAL_AXIS(axisPtr)
  699.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  700.  
  701.     /* Should really figure out a good offset value and test for that
  702.      * because we could still generate bogus numbers */
  703.     if (coord >= SHRT_MAX) {
  704.     coord = SHRT_MAX - 1000;
  705.     } else if (coord <= SHRT_MIN) {
  706.     coord = SHRT_MIN + 1000;
  707.     }
  708.     return (coord);
  709. }
  710.  
  711. /*
  712.  * ----------------------------------------------------------------------
  713.  *
  714.  * Blt_TransformPt --
  715.  *
  716.  *    Maps the given graph x,y coordinate values to a window position.
  717.  *
  718.  * Results:
  719.  *    Returns a XPoint structure containing the window coordinates of
  720.  *    the given graph x,y coordinate.
  721.  *
  722.  * ----------------------------------------------------------------------
  723.  */
  724. XPoint
  725. Blt_TransformPt(graphPtr, x, y, axisFlags)
  726.     Graph *graphPtr;
  727.     double x, y;
  728.     unsigned int axisFlags;    /* Specifies which axes to use */
  729. {
  730.     XPoint winPos;
  731.     enum AxisTypes axisType;
  732.  
  733.     axisType = (axisFlags & X1_AXIS_MASK) ? X1_AXIS : X2_AXIS;
  734.     winPos.x = Blt_Transform(graphPtr->axisArr[axisType], x);
  735.     axisType = (axisFlags & Y1_AXIS_MASK) ? Y1_AXIS : Y2_AXIS;
  736.     winPos.y = Blt_Transform(graphPtr->axisArr[axisType], y);
  737.     if (graphPtr->inverted) {    /* Swap x and y coordinates */
  738.     int coord;
  739.  
  740.     coord = winPos.y;
  741.     winPos.y = winPos.x;
  742.     winPos.x = coord;
  743.     }
  744.     return (winPos);
  745. }
  746.  
  747. /*
  748.  * ----------------------------------------------------------------------
  749.  *
  750.  * Blt_TransformDist --
  751.  *
  752.  *    Map the given graph x-coordinate value to a window position.
  753.  *
  754.  * Results:
  755.  *    Returns the window coordinate position at the given graph
  756.  *    x-coordinate.
  757.  *
  758.  * Note:
  759.  *    Since line and polygon clipping is performed by the X server,
  760.  *    we must be careful about coordinates which are outside of the
  761.  *      range of a signed short int.
  762.  *
  763.  * ----------------------------------------------------------------------
  764.  */
  765. int
  766. Blt_TransformDist(axis, value)
  767.     GraphAxis *axis;
  768.     double value;
  769. {
  770.     Axis *axisPtr = (Axis *)axis;
  771.     double norm;
  772.     int zero, coord;
  773.  
  774.     norm = Scale(axisPtr, 0.0);
  775.     zero = HORIZONTAL_AXIS(axisPtr)
  776.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  777.     norm = Scale(axisPtr, value);
  778.     coord = HORIZONTAL_AXIS(axisPtr)
  779.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  780.     coord -= zero;
  781.     return (BLT_ABS(coord));
  782. }
  783.  
  784. /*
  785.  * ----------------------------------------------------------------------
  786.  *
  787.  * Blt_PointOnGraph --
  788.  *
  789.  *    Determines if the window coordinates given represent a point
  790.  *    on the graph (within the bounds of the graph axes).
  791.  *
  792.  * Results:
  793.  *    Returns 1 is the point is within the bounds of the graph,
  794.  *    0 otherwise.
  795.  *
  796.  * ----------------------------------------------------------------------
  797.  */
  798. int
  799. Blt_PointOnGraph(graphPtr, pointPtr)
  800.     Graph *graphPtr;
  801.     XPoint *pointPtr;
  802. {
  803.     double norm;
  804.     Axis *axisPtr;
  805.  
  806.     /*
  807.      * It doesn't make a difference which x-axis or y-axis we use
  808.      * (the point is referenced by window coordinates). We're just
  809.      * checking if the point's within the range of axis' normalized
  810.      * coordinates [0..1].
  811.      */
  812.     axisPtr = (Axis *)graphPtr->bottomAxis;
  813.     if (axisPtr->scale == 0.0) {
  814.     return 0;        /* Axis layout hasn't been calculated yet */
  815.     }
  816.     norm = (pointPtr->x - axisPtr->offset) / axisPtr->scale;
  817.     if ((norm < 0.0) || (norm > 1.0)) {
  818.     return 0;        /* x-coordinates are off the graph */
  819.     }
  820.     axisPtr = (Axis *)graphPtr->leftAxis;
  821.     norm = (axisPtr->offset - pointPtr->y) / axisPtr->scale;
  822.     return ((norm >= 0.0) && (norm <= 1.0));
  823. }
  824.  
  825. /*
  826.  * ----------------------------------------------------------------------
  827.  *
  828.  * UpdateLimits --
  829.  *
  830.  *    Updates the min and max values for each axis as determined by
  831.  *    the data elements currently to be displayed.
  832.  *
  833.  * Results:
  834.  *    None.
  835.  *
  836.  * Side Effects:
  837.  *    Minimum, maximum data limit fields for both the X and Y axes
  838.  *    in the graph widget record are updated.
  839.  *
  840.  * ----------------------------------------------------------------------
  841.  */
  842. static void
  843. UpdateLimits(graphPtr, axisPtr)
  844.     Graph *graphPtr;
  845.     Axis *axisPtr;
  846. {
  847.     int result;
  848.  
  849.     if ((!AXIS_CONFIG_MAX_SET(axisPtr)) || (!AXIS_CONFIG_MIN_SET(axisPtr))) {
  850.     register Element *elemPtr;
  851.     Blt_ListEntry *entryPtr;
  852.     register double min, max;
  853.     double elemMin, elemMax;
  854.     double value;
  855.  
  856.     /* Find the minimum and maximum values for all the elements
  857.        displayed */
  858.     min = Blt_posInfinity, max = Blt_negInfinity;
  859.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  860.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  861.         elemPtr = (Element *)Blt_GetListValue(entryPtr);
  862.         if ((*elemPtr->limitsProc) (graphPtr, elemPtr,
  863.             (GraphAxis *)axisPtr, &elemMin, &elemMax) > 0) {
  864.         if (min > elemMin) {
  865.             min = elemMin;
  866.         }
  867.         if (max < elemMax) {
  868.             max = elemMax;
  869.         }
  870.         }
  871.     }
  872.     /*
  873.      * When auto-scaling, the axis limits are the bounds of the
  874.      * element data.  If no data exists, set arbitrary limits (wrt
  875.      * to log/linear scale).
  876.      */
  877.     if (min == Blt_posInfinity) {
  878.         min = (axisPtr->logScale) ? 0.001 : -10.0;
  879.     }
  880.     if (max == Blt_negInfinity) {
  881.         max = 10.0;
  882.     }
  883.     /*
  884.      * Handle situations where only one limit is set.
  885.      */
  886.     value = min;
  887.     if (AXIS_CONFIG_MIN_SET(axisPtr)) {
  888.         min = value = axisPtr->limits[LMIN];
  889.     } else if (AXIS_CONFIG_MAX_SET(axisPtr)) {
  890.         max = value = axisPtr->limits[LMAX];
  891.     }
  892.     /* If there's no range of data (min >= max), manufacture one */
  893.     if (min >= max) {
  894.         if (value == 0.0) {
  895.         min = -0.1, max = 0.1;
  896.         } else {
  897.         double x;
  898.  
  899.         x = BLT_FABS(value) * 0.1;
  900.         min = value - x;
  901.         max = value + x;
  902.         }
  903.     }
  904.     if (!AXIS_CONFIG_MIN_SET(axisPtr)) {
  905.         axisPtr->limits[LMIN] = min;
  906.     }
  907.     if (!AXIS_CONFIG_MAX_SET(axisPtr)) {
  908.         axisPtr->limits[LMAX] = max;
  909.     }
  910.     }
  911.     /* Indicate if the axis limits have changed */
  912.     result = (axisPtr->limits[LMAX] != axisPtr->prevMax) ||
  913.     (axisPtr->limits[LMIN] != axisPtr->prevMin);
  914.     /* and save the previous minimum and maximum values */
  915.     axisPtr->prevMin = axisPtr->limits[LMIN];
  916.     axisPtr->prevMax = axisPtr->limits[LMAX];
  917.     if (result) {
  918.     axisPtr->flags |= AXIS_CONFIG_DIRTY;
  919.     }
  920. }
  921.  
  922. /*
  923.  * ----------------------------------------------------------------------
  924.  *
  925.  * NiceNum --
  926.  *
  927.  *     Taken from Paul Heckbert's "Nice Numbers for Graph Labels" in
  928.  *    Graphics Gems (pp 61-63).  Finds a "nice" number approximately
  929.  *    equal to x.  Round the number if round=1, take ceiling if round=0.
  930.  *
  931.  * ----------------------------------------------------------------------
  932.  */
  933. static double
  934. NiceNum(x, round)
  935.     double x;
  936.     int round;
  937. {
  938.     double exponX;        /* exponent of x */
  939.     double fractX;        /* fractional part of x */
  940.     double nf;            /* nice, rounded fraction */
  941.  
  942.     exponX = floor(log10(x));
  943.     fractX = x / BLT_EXP10(exponX);    /* between 1 and 10 */
  944.     if (round) {
  945.     if (fractX < 1.5) {
  946.         nf = 1.;
  947.     } else if (fractX < 3.0) {
  948.         nf = 2.;
  949.     } else if (fractX < 7.0) {
  950.         nf = 5.;
  951.     } else {
  952.         nf = 10.;
  953.     }
  954.     } else if (fractX <= 1.0) {
  955.     nf = 1.;
  956.     } else if (fractX <= 2.0) {
  957.     nf = 2.;
  958.     } else if (fractX <= 5.0) {
  959.     nf = 5.0;
  960.     } else {
  961.     nf = 10.0;
  962.     }
  963.     return (nf * BLT_EXP10(exponX));
  964. }
  965.  
  966. /*
  967.  * ----------------------------------------------------------------------
  968.  *
  969.  * LogAxis --
  970.  *
  971.  *     Determine the range and units of a log scaled axis.
  972.  *
  973.  *     Unless the axis limits are specified, the axis is scaled
  974.  *     automatically, where the smallest and largest major ticks
  975.  *     encompass the range of actual data values.  When an axis
  976.  *     limit is specified, that value represents the
  977.  *     smallest(min)/largest(max) value in the displayed range of
  978.  *     values.
  979.  *
  980.  *     Both manual and automatic scaling are affected by the
  981.  *     step used.  By default, the step is the largest
  982.  *     power of ten to divide the range in more than one piece.
  983.  *
  984.  *     Automatic scaling:
  985.  *       Find the smallest number of units which contain the range of
  986.  *       values.  The minimum and maximum major tick values will be
  987.  *       represent the range of values for the axis. This greatest
  988.  *       number of major ticks possible is 10.
  989.  *
  990.  *     Manual scaling:
  991.  *       Make the minimum and maximum data values the represent the
  992.  *       range of the values for the axis.  The minimum and maximum
  993.  *       major ticks will be inclusive of this range.  This provides
  994.  *       the largest area for plotting and the expected results when
  995.  *       the axis min and max values have be set by the user (.e.g zooming).
  996.  *       The maximum number of major ticks is 20.
  997.  *
  998.  *       For log scale, there is always the possibility that the minimum
  999.  *       and maximum data values are the same magnitude.  To represent
  1000.  *       the points properly, at least one full decade should be shown.
  1001.  *       However, if you zoom a log scale plot, the results should be
  1002.  *       predictable. Therefore, in that case, show only minor ticks.
  1003.  *       Lastly, there should be an appropriate way to handle numbers <=0.
  1004.  *
  1005.  *          maxY
  1006.  *            |    units = magnitude (of least significant digit)
  1007.  *            |    high  = largest unit tick < max axis value
  1008.  *      high _|    low   = smallest unit tick > min axis value
  1009.  *            |
  1010.  *            |    range = high - low
  1011.  *            |    # ticks = greatest factor of range/units
  1012.  *           _|
  1013.  *        U   |
  1014.  *        n   |
  1015.  *        i   |
  1016.  *        t  _|
  1017.  *            |
  1018.  *            |
  1019.  *            |
  1020.  *       low _|
  1021.  *            |
  1022.  *            |_minX________________maxX__
  1023.  *            |   |       |      |       |
  1024.  *     minY  low                        high
  1025.  *           minY
  1026.  *
  1027.  *
  1028.  *     numTicks = Number of ticks
  1029.  *     min = Minimum value of axis
  1030.  *     max = Maximum value of axis
  1031.  *     range    = Range of values (max - min)
  1032.  *
  1033.  *     If the number of decades is greater than ten, it is assumed
  1034.  *    that the full set of log-style ticks can't be drawn properly.
  1035.  *
  1036.  * Results:
  1037.  *    None
  1038.  *
  1039.  * ----------------------------------------------------------------------
  1040.  */
  1041. static void
  1042. LogAxis(axisPtr)
  1043.     Axis *axisPtr;
  1044. {
  1045.     double range;
  1046.     double min, max;
  1047.  
  1048.     min = axisPtr->limits[LMIN];
  1049.     max = axisPtr->limits[LMAX];
  1050.  
  1051.     if (min > 0.0) {
  1052.     min = floor(log10(min));
  1053.     } else {
  1054.     min = 0.0;
  1055.     }
  1056.     if (max > 0.0) {
  1057.     max = ceil(log10(max));
  1058.     } else {
  1059.     max = 1.0;
  1060.     }
  1061.     range = max - min;
  1062.     if (range > 10) {
  1063.     range = NiceNum(range, 0);
  1064.     axisPtr->step = NiceNum(range / (NTICK - 1), 1);
  1065.  
  1066.     /* Find the outer limits in terms of the step. */
  1067.     min = UFLOOR(min, axisPtr->step);
  1068.     max = UCEIL(max, axisPtr->step);
  1069.     axisPtr->numTicks = (int)((max - min) / axisPtr->step) + 1;
  1070.     axisPtr->subStep = BLT_EXP10(floor(log10(axisPtr->step)));
  1071.  
  1072.     if (axisPtr->step == axisPtr->subStep) {
  1073.         axisPtr->subTicks = 5;
  1074.         axisPtr->subStep = axisPtr->step * 0.2;
  1075.     } else {
  1076.         axisPtr->subTicks = BLT_RND(axisPtr->step / axisPtr->subStep);
  1077.     }
  1078.     } else {
  1079.     if (min == max) {
  1080.         max++;
  1081.     }
  1082.     axisPtr->numTicks = (int)((max - min) + 1);
  1083.     axisPtr->step = 1.0;
  1084.     axisPtr->subTicks = 10;
  1085.     }
  1086.     axisPtr->min = axisPtr->tickMin = min;
  1087.     axisPtr->max = axisPtr->tickMax = max;
  1088.     axisPtr->range = (max - min);
  1089. #ifdef notdef
  1090.     fprintf(stderr, "Major: %s\nRegion min=%g,max=%g\nTick min=%g,max=%g\n\
  1091. numTicks=%d, range=%g, step=%.15g\n", axisNames[axisPtr->type], min, max,
  1092.     axisPtr->tickMin, axisPtr->tickMax, axisPtr->numTicks,
  1093.     axisPtr->range, axisPtr->step);
  1094.     fprintf(stderr, "Minor numTicks=%d, step=%.15g\n\n",
  1095.     axisPtr->subTicks, axisPtr->subStep);
  1096. #endif
  1097. }
  1098.  
  1099. /*
  1100.  * ----------------------------------------------------------------------
  1101.  *
  1102.  * LinearAxis --
  1103.  *
  1104.  *     Determine the units of a linear scaled axis.
  1105.  *
  1106.  *     Unless the axis limits are specified, the axis is scaled
  1107.  *     automatically, where the smallest and largest major ticks
  1108.  *     encompass the range of actual data values.  When an axis
  1109.  *     limit is specified, that value represents the
  1110.  *     smallest(min)/largest(max) value in the displayed range of
  1111.  *     values.
  1112.  *
  1113.  *     Both manual and automatic scaling are affected by the
  1114.  *     step used.  By default, the step is the largest
  1115.  *     power of ten to divide the range in more than one piece.
  1116.  *
  1117.  *     Automatic scaling:
  1118.  *       Find the smallest number of units which contain the range of
  1119.  *       values.  The minimum and maximum major tick values will be
  1120.  *       represent the range of values for the axis. This greatest
  1121.  *       number of major ticks possible is 10.
  1122.  *
  1123.  *     Manual scaling:
  1124.  *       Make the minimum and maximum data values the represent the
  1125.  *       range of the values for the axis.  The minimum and maximum
  1126.  *       major ticks will be inclusive of this range.  This provides
  1127.  *       the largest area for plotting and the expected results when
  1128.  *       the axis min and max values have be set by the user (.e.g zooming).
  1129.  *       The maximum number of major ticks is 20.
  1130.  *
  1131.  *       For log scale, there is always the possibility that the minimum
  1132.  *       and maximum data values are the same magnitude.  To represent
  1133.  *       the points properly, at least one full decade should be shown.
  1134.  *       However, if you zoom a log scale plot, the results should be
  1135.  *       predictable. Therefore, in that case, show only minor ticks.
  1136.  *       Lastly, there should be an appropriate way to handle numbers <=0.
  1137.  *
  1138.  *          maxY
  1139.  *            |    units = magnitude (of least significant digit)
  1140.  *            |    high  = largest unit tick < max axis value
  1141.  *      high _|    low   = smallest unit tick > min axis value
  1142.  *            |
  1143.  *            |    range = high - low
  1144.  *            |    # ticks = greatest factor of range/units
  1145.  *           _|
  1146.  *        U   |
  1147.  *        n   |
  1148.  *        i   |
  1149.  *        t  _|
  1150.  *            |
  1151.  *            |
  1152.  *            |
  1153.  *       low _|
  1154.  *            |
  1155.  *            |_minX________________maxX__
  1156.  *            |   |       |      |       |
  1157.  *     minY  low                        high
  1158.  *           minY
  1159.  *
  1160.  *
  1161.  *     numTicks = Number of ticks
  1162.  *     min = Minimum value of axis
  1163.  *     max = Maximum value of axis
  1164.  *     range    = Range of values (max - min)
  1165.  *
  1166.  * Results:
  1167.  *    None.
  1168.  *
  1169.  * ----------------------------------------------------------------------
  1170.  */
  1171. static void
  1172. LinearAxis(axisPtr)
  1173.     Axis *axisPtr;
  1174. {
  1175.     double range, unit, pad;
  1176.     double min, max;
  1177.  
  1178.     min = axisPtr->limits[LMIN];
  1179.     max = axisPtr->limits[LMAX];
  1180.  
  1181.     /*
  1182.      * Calculate the major step.
  1183.      */
  1184.     range = max - min;
  1185.  
  1186.     if ((axisPtr->reqStep > 0.0) && (axisPtr->reqStep < range)) {
  1187.     axisPtr->step = axisPtr->reqStep;
  1188.     } else {
  1189.     range = NiceNum(range, 0);
  1190.     axisPtr->step = NiceNum(range / (NTICK - 1), 1);
  1191.     }
  1192.  
  1193.     /*
  1194.      * Find the outer tick values in terms of the major step interval.
  1195.      * Add +0.0 to preclude the possibility of an IEEE -0.0.
  1196.      */
  1197.  
  1198.     axisPtr->tickMin = UFLOOR(min, axisPtr->step) + 0.0;
  1199.     axisPtr->tickMax = UCEIL(max, axisPtr->step) + 0.0;
  1200.     range = axisPtr->tickMax - axisPtr->tickMin;
  1201.     unit = range / axisPtr->step;
  1202.     axisPtr->numTicks = BLT_RND(unit) + 1;
  1203.  
  1204.     /*
  1205.      * If the axis is "loose", the range is between the two outermost
  1206.      * ticks. Otherwise if it's "tight", the range is between the data
  1207.      * min and max.
  1208.      */
  1209.     if (axisPtr->loose) {
  1210.     axisPtr->min = axisPtr->tickMin, axisPtr->max = axisPtr->tickMax;
  1211.     } else {
  1212.     axisPtr->min = min, axisPtr->max = max;
  1213.     }
  1214.  
  1215.     /*
  1216.      * If is a limit is auto-scaled, add some padding so that the
  1217.      * symbols representing data points at the extremes aren't clipped
  1218.      * in half by the edge of the plot.  Two percent is an arbitrary
  1219.      * guess.
  1220.      */
  1221.  
  1222.     pad = (axisPtr->max - axisPtr->min) * 0.02;
  1223.     if (!AXIS_CONFIG_MIN_SET(axisPtr)) {
  1224.     axisPtr->min -= pad;
  1225.     }
  1226.     if (!AXIS_CONFIG_MAX_SET(axisPtr)) {
  1227.     axisPtr->max += pad;
  1228.     }
  1229.     axisPtr->range = axisPtr->max - axisPtr->min;
  1230.  
  1231. #ifdef notdef
  1232.     fprintf(stderr, "Major: %s\nRegion min=%g,max=%g\nTick min=%g,max=%g\n\
  1233. numTicks=%d, range=%g, step=%.15g\n", axisNames[axisPtr->type], min, max,
  1234.     axisPtr->tickMin, axisPtr->tickMax, axisPtr->numTicks,
  1235.     axisPtr->range, axisPtr->step);
  1236. #endif
  1237.  
  1238.     /* Now calculate the minor tick step and number. */
  1239.     axisPtr->subTicks = axisPtr->reqSubTicks;
  1240.     if (axisPtr->subTicks < 0) {
  1241.     axisPtr->subTicks = 0;
  1242.     }
  1243.     if (axisPtr->subTicks > 0) {
  1244.     axisPtr->subStep = axisPtr->step / axisPtr->subTicks;
  1245.     } else {
  1246.     axisPtr->subStep = axisPtr->step * 0.2;    /* Need this for layout */
  1247.     }
  1248. #ifdef notdef
  1249.     fprintf(stderr, "Minor numTicks=%d, step=%.15g\n\n",
  1250.     axisPtr->subTicks, axisPtr->subStep);
  1251. #endif
  1252. }
  1253.  
  1254. /*
  1255.  * -----------------------------------------------------------------
  1256.  *
  1257.  * SetAxisLimits  --
  1258.  *
  1259.  * -----------------------------------------------------------------
  1260.  */
  1261. static void
  1262. SetAxisLimits(graphPtr, axisPtr)
  1263.     Graph *graphPtr;
  1264.     Axis *axisPtr;
  1265. {
  1266.     UpdateLimits(graphPtr, axisPtr);
  1267.     /*
  1268.      * For barcharts, adjust the min or max values to include 0.0
  1269.      * if the bars are drawn along this axis.
  1270.      */
  1271.     if ((graphPtr->type == BARCHART) && (Y_AXIS(axisPtr))) {
  1272.     if (!AXIS_CONFIG_MIN_SET(axisPtr) && (axisPtr->limits[LMIN] > 0.0)) {
  1273.         axisPtr->limits[LMIN] = 0.0;
  1274.     }
  1275.     if (!AXIS_CONFIG_MAX_SET(axisPtr) && (axisPtr->limits[LMAX] < 0.0)) {
  1276.         axisPtr->limits[LMAX] = 0.0;
  1277.     }
  1278.     }
  1279.     if (axisPtr->flags & AXIS_CONFIG_DIRTY) {
  1280.     /* Calculate min/max tick (major/minor) layouts */
  1281.     if (axisPtr->logScale) {
  1282.         LogAxis(axisPtr);
  1283.     } else {
  1284.         LinearAxis(axisPtr);
  1285.     }
  1286.     axisPtr->flags &= ~AXIS_CONFIG_DIRTY;
  1287.  
  1288.     /* When any axis changes, we need to layout the entire graph. */
  1289.     graphPtr->flags |= (LAYOUT_ALL | DIRTY | REFRESH);
  1290.     }
  1291. }
  1292.  
  1293. /*
  1294.  * ----------------------------------------------------------------------
  1295.  *
  1296.  * Blt_ComputeAxes --
  1297.  *
  1298.  * Results:
  1299.  *    None.
  1300.  *
  1301.  * ----------------------------------------------------------------------
  1302.  */
  1303. void
  1304. Blt_ComputeAxes(graphPtr)
  1305.     Graph *graphPtr;
  1306. {
  1307.     register int i;
  1308.  
  1309.     for (i = 0; i < 4; i++) {
  1310.     SetAxisLimits(graphPtr, (Axis *)graphPtr->axisArr[i]);
  1311.     }
  1312. }
  1313.  
  1314. /*
  1315.  * ----------------------------------------------------------------------
  1316.  *
  1317.  * ConfigureAxis --
  1318.  *
  1319.  *    Configures axis attributes (font, line width, label, etc) and
  1320.  *    allocates a new (possibly shared) graphics context.  Line cap
  1321.  *    style is projecting.  This is for the problem of when a tick
  1322.  *    sits directly at the end point of the axis.
  1323.  *
  1324.  * Results:
  1325.  *    The return value is a standard Tcl result.
  1326.  *
  1327.  * Side Effects:
  1328.  *    Axis resources are allocated (GC, font). Axis layout is
  1329.  *    deferred until the height and width of the window are known.
  1330.  *
  1331.  * ----------------------------------------------------------------------
  1332.  */
  1333. static int
  1334. ConfigureAxis(graphPtr, axisPtr, argc, argv, flags)
  1335.     Graph *graphPtr;
  1336.     Axis *axisPtr;
  1337.     int argc;
  1338.     char *argv[];
  1339.     int flags;
  1340. {
  1341.     GC newGC;
  1342.     XGCValues gcValues;
  1343.     unsigned long gcMask;
  1344.     Tk_ConfigSpec *configSpecs;
  1345.  
  1346.     configSpecs = axisConfigSpecs[axisPtr->type];
  1347.     if (flags & TK_CONFIG_ARGV_ONLY) {
  1348.     if (argc == 0) {
  1349.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1350.             configSpecs, (char *)axisPtr, (char *)NULL, flags));
  1351.     } else if (argc == 1) {
  1352.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1353.             configSpecs, (char *)axisPtr, argv[0], flags));
  1354.     }
  1355.     }
  1356.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  1357.         argc, argv, (char *)axisPtr, flags) != TCL_OK) {
  1358.     return TCL_ERROR;
  1359.     }
  1360.     /*
  1361.      * Check requested X and Y axis limits. Can't allow min to be
  1362.      * greater than max, or have undefined log scale limits.
  1363.      */
  1364.     if ((AXIS_CONFIG_MIN_SET(axisPtr)) && (AXIS_CONFIG_MAX_SET(axisPtr)) &&
  1365.     (axisPtr->limits[LMIN] >= axisPtr->limits[LMAX])) {
  1366.     sprintf(graphPtr->interp->result,
  1367.         "impossible %s-axis limits (min %g >= max %g)",
  1368.         axisNames[axisPtr->type], axisPtr->limits[LMIN],
  1369.         axisPtr->limits[LMAX]);
  1370.     return TCL_ERROR;
  1371.     }
  1372.     if ((axisPtr->logScale) && (AXIS_CONFIG_MIN_SET(axisPtr)) &&
  1373.     (axisPtr->limits[LMIN] <= 0.0)) {
  1374.     sprintf(graphPtr->interp->result,
  1375.         "invalid %s-axis limits (min=%g,max=%g) for log scale",
  1376.         axisNames[axisPtr->type], axisPtr->limits[LMIN],
  1377.         axisPtr->limits[LMAX]);
  1378.     return TCL_ERROR;
  1379.     }
  1380.     /*
  1381.      * Reset bogus line widths to zero. Can't allow bad line widths
  1382.      * because the layout routines compute axis and tick positions
  1383.      * with them.
  1384.      */
  1385.     if (axisPtr->lineWidth < 1) {
  1386.     axisPtr->lineWidth = 0;
  1387.     }
  1388.     /*
  1389.      * Create an unshared GC for the tick labels. The GC is private
  1390.      * because the labels may be rotated, requiring the GCStipple and
  1391.      * GCTSOffset fields to change.
  1392.      */
  1393.     gcMask = GCForeground | GCFont;
  1394.     gcValues.font = axisPtr->fontPtr->fid;
  1395.     gcValues.foreground = axisPtr->fgColorPtr->pixel;
  1396.     if (graphPtr->border != NULL) {
  1397.     gcValues.background = Tk_3DBorderColor(graphPtr->border)->pixel;
  1398.     gcMask |= GCBackground;
  1399.     }
  1400.     newGC = XCreateGC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
  1401.     gcMask, &gcValues);
  1402.     if (axisPtr->textGC != NULL) {
  1403.     XFreeGC(graphPtr->display, axisPtr->textGC);
  1404.     }
  1405.     axisPtr->textGC = newGC;
  1406.  
  1407.     /* Create GC for axis line and ticks. */
  1408.  
  1409.     gcMask = GCForeground | GCLineWidth | GCCapStyle;
  1410.     gcValues.line_width = axisPtr->lineWidth;
  1411.     gcValues.cap_style = CapProjecting;
  1412.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  1413.     if (axisPtr->lineGC != NULL) {
  1414.     Tk_FreeGC(graphPtr->display, axisPtr->lineGC);
  1415.     }
  1416.     axisPtr->lineGC = newGC;
  1417.  
  1418.     /*
  1419.      * Don't bother to check what options have changed.  Almost every
  1420.      * axis configuration option changes the size of the plotting area
  1421.      * (except for -foreground).
  1422.      */
  1423.     graphPtr->flags |= LAYOUT_ALL;
  1424.     axisPtr->flags |= AXIS_CONFIG_DIRTY;
  1425.     SetAxisLimits(graphPtr, axisPtr);
  1426.     Blt_RedrawGraph(graphPtr);
  1427.     return TCL_OK;
  1428. }
  1429.  
  1430. /*
  1431.  * ----------------------------------------------------------------------
  1432.  *
  1433.  * DestroyAxis --
  1434.  *
  1435.  * Results:
  1436.  *    None.
  1437.  *
  1438.  * Side effects:
  1439.  *    Resources (font, color, gc, labels, etc.) associated with the
  1440.  *    axis are deallocated.
  1441.  *
  1442.  * ----------------------------------------------------------------------
  1443.  */
  1444. static void
  1445. DestroyAxis(graphPtr, axis)
  1446.     Graph *graphPtr;
  1447.     GraphAxis *axis;
  1448. {
  1449.     Axis *axisPtr = (Axis *)axis;
  1450.  
  1451.     Tk_FreeOptions(axisConfigSpecs[axisPtr->type], (char *)axisPtr,
  1452.     graphPtr->display, 0);
  1453.  
  1454.     if (axisPtr->lineGC != NULL) {
  1455.     Tk_FreeGC(graphPtr->display, axisPtr->lineGC);
  1456.     }
  1457.     if (axisPtr->textGC != NULL) {
  1458.     XFreeGC(graphPtr->display, axisPtr->textGC);
  1459.     }
  1460.     if (axisPtr->labelArr != NULL) {
  1461.     free((char *)axisPtr->labelArr);
  1462.     }
  1463.     if (axisPtr->segArr != NULL) {
  1464.     free((char *)axisPtr->segArr);
  1465.     }
  1466.     free((char *)axisPtr);
  1467. }
  1468.  
  1469. /*
  1470.  * ----------------------------------------------------------------------
  1471.  *
  1472.  * CalculateOffsets --
  1473.  *
  1474.  *    Determines the placements of the axis, major and minor ticks,
  1475.  *    and title of the axis.
  1476.  *
  1477.  * Results:
  1478.  *    None.
  1479.  *
  1480.  * ----------------------------------------------------------------------
  1481.  */
  1482. static void
  1483. CalculateOffsets(graphPtr, axisPtr)
  1484.     Graph *graphPtr;
  1485.     Axis *axisPtr;
  1486. {
  1487.     int pad;            /* Offset of axis from interior region. This
  1488.                  * includes a possible border and the axis
  1489.                  * line width. */
  1490.     unsigned int textHeight;
  1491.     int innerPos, outerPos;
  1492.     int majorOffset, minorOffset, labelOffset, titleOffset;
  1493.  
  1494.     textHeight = TEXTHEIGHT(graphPtr->fontPtr);
  1495.  
  1496.     titleOffset = graphPtr->borderWidth + textHeight;
  1497.     majorOffset = BLT_ABS(axisPtr->tickLength);
  1498.     minorOffset = BLT_RND(majorOffset * 0.5);
  1499.     labelOffset = BLT_RND(majorOffset * 1.4) + axisPtr->lineWidth / 2;
  1500.  
  1501.     /* Adjust offset for the interior border width and the line width */
  1502.     pad = graphPtr->plotBW + axisPtr->lineWidth + 2;
  1503.     if (graphPtr->plotBW > 0) {
  1504.     pad++;
  1505.     }
  1506.     if ((axisPtr->location == LEFT_AXIS) || (axisPtr->location == TOP_AXIS)) {
  1507.     majorOffset = -majorOffset;
  1508.     minorOffset = -minorOffset;
  1509.     labelOffset = -labelOffset;
  1510.     }
  1511.     /*
  1512.      * Pre-calculate the x-coordinate positions of the axis,
  1513.      * tick labels, and the individual major and minor ticks.
  1514.      */
  1515.     switch (axisPtr->location) {
  1516.     case BOTTOM_AXIS:
  1517.     innerPos = graphPtr->origin.y + pad;
  1518.     axisPtr->titlePos.x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1519.     axisPtr->titlePos.y = graphPtr->height - titleOffset;
  1520.     axisPtr->anchor = TK_ANCHOR_N;
  1521.     break;
  1522.  
  1523.     case LEFT_AXIS:
  1524.     innerPos = graphPtr->origin.x - pad;
  1525.     axisPtr->titlePos.x = titleOffset;
  1526.     axisPtr->titlePos.y = (graphPtr->origin.y + graphPtr->extreme.y) / 2;
  1527.     axisPtr->anchor = TK_ANCHOR_E;
  1528.     break;
  1529.  
  1530.     case TOP_AXIS:
  1531.     innerPos = graphPtr->extreme.y - pad;
  1532.     axisPtr->titlePos.x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1533.     axisPtr->titlePos.y = titleOffset;
  1534.     if (graphPtr->title != NULL) {
  1535.         axisPtr->titlePos.y += (2 * textHeight);
  1536.     } else {
  1537.         axisPtr->titlePos.y += (textHeight / 2);
  1538.     }
  1539.     axisPtr->anchor = TK_ANCHOR_S;
  1540.     break;
  1541.  
  1542.     case RIGHT_AXIS:
  1543.     innerPos = graphPtr->extreme.x + pad;
  1544.     axisPtr->titlePos.x = graphPtr->width -
  1545.         (graphPtr->legendPtr->width + titleOffset);
  1546.     axisPtr->titlePos.y = (graphPtr->origin.y + graphPtr->extreme.y) / 2;
  1547.     axisPtr->anchor = TK_ANCHOR_W;
  1548.     break;
  1549.  
  1550.     default:
  1551.     abort();
  1552.     }
  1553.     outerPos = innerPos + majorOffset;
  1554.     axisPtr->posArr[MAJOR_TICK] = outerPos;
  1555.     axisPtr->posArr[AXIS_LINE] = innerPos;
  1556.     axisPtr->posArr[MINOR_TICK] = innerPos + minorOffset;
  1557.     axisPtr->posArr[TICK_LABEL] = innerPos + labelOffset;
  1558.     if (axisPtr->tickLength < 0) {
  1559.     axisPtr->posArr[MAJOR_TICK] = innerPos;
  1560.     axisPtr->posArr[AXIS_LINE] = outerPos;
  1561.     }
  1562. }
  1563.  
  1564. static XSegment
  1565. AxisLine(axisPtr, min, max)
  1566.     Axis *axisPtr;        /* Axis information */
  1567.     double min, max;        /* Limits of axis in graph coordinates */
  1568. {
  1569.     double normMin, normMax;
  1570.     XSegment segment;
  1571.  
  1572.     normMax = NORM(axisPtr, min);
  1573.     if (axisPtr->descending) {
  1574.     normMax = 1.0 - normMax;
  1575.     }
  1576.     normMin = NORM(axisPtr, max);
  1577.     if (axisPtr->descending) {
  1578.     normMin = 1.0 - normMin;
  1579.     }
  1580.     if (HORIZONTAL_AXIS(axisPtr)) {
  1581.     segment.y1 = segment.y2 = axisPtr->posArr[AXIS_LINE];
  1582.     segment.x1 = MAPX(axisPtr, normMin);
  1583.     segment.x2 = MAPX(axisPtr, normMax);
  1584.     } else {
  1585.     segment.x1 = segment.x2 = axisPtr->posArr[AXIS_LINE];
  1586.     segment.y1 = MAPY(axisPtr, normMin);
  1587.     segment.y2 = MAPY(axisPtr, normMax);
  1588.     }
  1589.     return (segment);
  1590. }
  1591.  
  1592.  
  1593. static XSegment
  1594. Tick(axisPtr, value, whichTick)
  1595.     Axis *axisPtr;
  1596.     double value;
  1597.     int whichTick;        /* If non-zero, create minor tick instead */
  1598. {
  1599.     double norm;
  1600.     XSegment segment;
  1601.     int tick;
  1602.  
  1603.     norm = NORM(axisPtr, value);
  1604.     if (axisPtr->descending) {
  1605.     norm = 1.0 - norm;
  1606.     }
  1607.     tick = axisPtr->posArr[whichTick];
  1608.     if (HORIZONTAL_AXIS(axisPtr)) {
  1609.     segment.y1 = axisPtr->posArr[AXIS_LINE];
  1610.     segment.y2 = tick;
  1611.     segment.x1 = segment.x2 = MAPX(axisPtr, norm);
  1612.     } else {
  1613.     segment.x1 = axisPtr->posArr[AXIS_LINE];
  1614.     segment.x2 = tick;
  1615.     segment.y1 = segment.y2 = MAPY(axisPtr, norm);
  1616.     }
  1617.     return (segment);
  1618. }
  1619.  
  1620. /*
  1621.  * -----------------------------------------------------------------
  1622.  *
  1623.  * LayoutAxis --
  1624.  *
  1625.  *    Pre-calculate the x-coordinate positions of the axis, ticks
  1626.  *    and labels to be used later when displaying the X axis.  Ticks
  1627.  *    (minor and major) will be saved in an array of XSegments so
  1628.  *    that they can be drawn in one XDrawSegments call. The strings
  1629.  *    representing the tick labels and the corresponding window
  1630.  *    positions are saved in an array of Label's.
  1631.  *
  1632.  *      Calculates the values for each major and minor tick and checks to
  1633.  *    see if they are in range (the outer ticks may be outside of the
  1634.  *    range of plotted values).
  1635.  *
  1636.  * Results:
  1637.  *    None.
  1638.  *
  1639.  * SideEffects:
  1640.  *    Line segments and tick labels saved will be used to draw
  1641.  *    the X axis.
  1642.  * ----------------------------------------------------------------- */
  1643. static void
  1644. LayoutAxis(graphPtr, axis)
  1645.     Graph *graphPtr;
  1646.     GraphAxis *axis;
  1647. {
  1648.     Axis *axisPtr = (Axis *)axis;
  1649.     XSegment *segArr;
  1650.     unsigned int arraySize;
  1651.     double min, max;
  1652.     double value, subValue;
  1653.     register int i, j;
  1654.     register int sgmts, labels;
  1655.     double epsilon;
  1656.     static float logTable[] =    /* Precomputed log10 values [1..10] */
  1657.     {
  1658.     0.0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1.0
  1659.     };
  1660.  
  1661.     CalculateOffsets(graphPtr, axisPtr);
  1662.  
  1663.     /* Save all line coordinates in an array of line segments. */
  1664.  
  1665.     arraySize = (1 + (axisPtr->numTicks * (axisPtr->subTicks + 1)));
  1666.     segArr = (XSegment *)malloc(arraySize * sizeof(XSegment));
  1667.     if (segArr == NULL) {
  1668.     return;            /* Can't allocate array of segments */
  1669.     }
  1670.     if ((axisPtr->logScale) || (axisPtr->loose) ||
  1671.     (axisPtr->limits[LMIN] == axisPtr->limits[LMAX])) {
  1672.     min = axisPtr->tickMin, max = axisPtr->tickMax;
  1673.     } else {
  1674.     min = axisPtr->limits[LMIN];
  1675.     max = axisPtr->limits[LMAX];
  1676.     }
  1677.  
  1678.     /* Axis baseline */
  1679.     segArr[0] = AxisLine(axisPtr, min, max);
  1680.  
  1681.     sgmts = 1, labels = 0;
  1682.     if (!axisPtr->showTicks) {
  1683.     goto done;        /* Only display axis line */
  1684.     }
  1685.     /* Use numbers just beyond the limits when testing for equality */
  1686.     epsilon = 2 * MIN_DBL_VALUE;
  1687.     min -= epsilon, max += epsilon;
  1688.  
  1689.     value = axisPtr->tickMin;    /* Start from smallest axis tick */
  1690.     for (i = 0; i < axisPtr->numTicks; i++) {
  1691.     subValue = value = UROUND(value, axisPtr->step);
  1692.  
  1693.     /* Minor ticks */
  1694.     for (j = 1; j < axisPtr->subTicks; j++) {
  1695.         if ((axisPtr->logScale) && (axisPtr->step == 1.0)) {
  1696.         subValue = value + (double)logTable[j];
  1697.         } else {
  1698.         subValue += axisPtr->subStep;
  1699.         }
  1700.         if ((subValue >= min) && (subValue <= max)) {
  1701.         segArr[sgmts] = Tick(axisPtr, subValue, MINOR_TICK);
  1702.         sgmts++;
  1703.         }
  1704.     }
  1705.  
  1706.     /* Major tick and label */
  1707.     if ((value >= min) && (value <= max)) {
  1708.         short int labelPos;
  1709.  
  1710.         segArr[sgmts] = Tick(axisPtr, value, MAJOR_TICK);
  1711.         labelPos = (short int)axisPtr->posArr[TICK_LABEL];
  1712.  
  1713.         /* Save tick label position */
  1714.  
  1715.         if (HORIZONTAL_AXIS(axisPtr)) {
  1716.         axisPtr->labelArr[labels].x = segArr[sgmts].x1;
  1717.         axisPtr->labelArr[labels].y = labelPos;
  1718.         } else {
  1719.         axisPtr->labelArr[labels].x = labelPos;
  1720.         axisPtr->labelArr[labels].y = segArr[sgmts].y1;
  1721.         }
  1722.         sgmts++, labels++;
  1723.     }
  1724.     value += axisPtr->step;
  1725.     }
  1726.  
  1727.   done:
  1728.  
  1729.     assert(sgmts <= arraySize);
  1730.     assert(labels <= axisPtr->numLabels);
  1731.  
  1732.     if (axisPtr->segArr != NULL) {
  1733.     free((char *)axisPtr->segArr);
  1734.     }
  1735.     axisPtr->segArr = segArr;
  1736.     axisPtr->numSegments = sgmts;
  1737. }
  1738.  
  1739. /*
  1740.  * -----------------------------------------------------------------
  1741.  *
  1742.  * DisplayAxis --
  1743.  *
  1744.  *    Draws the axis, ticks, and labels onto the canvas.
  1745.  *
  1746.  *    Initializes and passes text attribute information through
  1747.  *    TextAttributes structure.
  1748.  *
  1749.  * Results:
  1750.  *    None.
  1751.  *
  1752.  * Side Effects:
  1753.  *    Axis gets drawn on window.
  1754.  *
  1755.  * -----------------------------------------------------------------
  1756.  */
  1757.  
  1758. static float titleRot[4] =    /* Rotation for each axis title */
  1759. {
  1760.     0.0, 90.0, 0.0, 270.0
  1761. };
  1762.  
  1763. static void
  1764. DisplayAxis(graphPtr, axis, attrPtr)
  1765.     Graph *graphPtr;
  1766.     GraphAxis *axis;
  1767.     TextAttributes *attrPtr;
  1768. {
  1769.     Axis *axisPtr = (Axis *)axis;
  1770.  
  1771.     if (axisPtr->title != NULL) {
  1772.     attrPtr->theta = (double)titleRot[axisPtr->location];
  1773.     Blt_DrawText(graphPtr->display, graphPtr->canvas, axisPtr->title,
  1774.         attrPtr, axisPtr->titlePos.x, axisPtr->titlePos.y);
  1775.     }
  1776.     if (axisPtr->showTicks) {
  1777.     register int i;
  1778.     TextAttributes textAttr;
  1779.  
  1780.     /* Setup static text attribute information */
  1781.  
  1782.     textAttr.theta = axisPtr->theta;
  1783.     textAttr.anchor = axisPtr->anchor;
  1784.     textAttr.fontPtr = axisPtr->fontPtr;
  1785.     textAttr.fgColorPtr = axisPtr->fgColorPtr;
  1786.     textAttr.bgColorPtr = Tk_3DBorderColor(graphPtr->border);
  1787.     textAttr.gc = axisPtr->textGC;
  1788.  
  1789.     /* Draw the ticks labels and then the ticks and axis */
  1790.     for (i = 0; i < axisPtr->numLabels; i++) {
  1791.         Blt_DrawText(graphPtr->display, graphPtr->canvas,
  1792.         axisPtr->labelArr[i].text, &textAttr,
  1793.         axisPtr->labelArr[i].x, axisPtr->labelArr[i].y);
  1794.     }
  1795.     }
  1796.     if (axisPtr->numSegments > 0) {
  1797.     XDrawSegments(graphPtr->display, graphPtr->canvas, axisPtr->lineGC,
  1798.         axisPtr->segArr, axisPtr->numSegments);
  1799.     }
  1800. }
  1801.  
  1802. /*
  1803.  * -----------------------------------------------------------------
  1804.  *
  1805.  * PrintAxis --
  1806.  *
  1807.  *    Generates PostScript output to draw the axis, ticks, and
  1808.  *    labels.
  1809.  *
  1810.  *    Initializes and passes text attribute information through
  1811.  *    TextAttributes structure.
  1812.  *
  1813.  * Results:
  1814.  *    None.
  1815.  *
  1816.  * Side Effects:
  1817.  *    PostScript output is left in graphPtr->interp->result;
  1818.  *
  1819.  * -----------------------------------------------------------------
  1820.  */
  1821. static void
  1822. PrintAxis(graphPtr, axis, attrPtr)
  1823.     Graph *graphPtr;
  1824.     GraphAxis *axis;
  1825.     TextAttributes *attrPtr;
  1826. {
  1827.     Axis *axisPtr = (Axis *)axis;
  1828.  
  1829.     if (axisPtr->title != NULL) {
  1830.     attrPtr->theta = (double)titleRot[axisPtr->location];
  1831.     Blt_TextToPostScript(graphPtr, axisPtr->title, attrPtr,
  1832.         axisPtr->titlePos.x, axisPtr->titlePos.y);
  1833.     }
  1834.     if (axisPtr->showTicks) {
  1835.     TextAttributes textAttr;
  1836.     register int i;
  1837.  
  1838.     /* Setup static text attribute information */
  1839.  
  1840.     textAttr.theta = axisPtr->theta;
  1841.     textAttr.anchor = axisPtr->anchor;
  1842.     textAttr.fontPtr = axisPtr->fontPtr;
  1843.     textAttr.fgColorPtr = axisPtr->fgColorPtr;
  1844.     textAttr.bgColorPtr = (XColor *)NULL;
  1845.  
  1846.     for (i = 0; i < axisPtr->numLabels; i++) {
  1847.         Blt_TextToPostScript(graphPtr, axisPtr->labelArr[i].text,
  1848.         &textAttr, axisPtr->labelArr[i].x, axisPtr->labelArr[i].y);
  1849.     }
  1850.     }
  1851.     if (axisPtr->numSegments > 0) {
  1852.     Blt_SetLineAttributes(graphPtr, axisPtr->fgColorPtr,
  1853.         axisPtr->lineWidth, 0);
  1854.     Blt_SegmentsToPostScript(graphPtr, axisPtr->segArr,
  1855.         axisPtr->numSegments);
  1856.     }
  1857. }
  1858.  
  1859. static void
  1860. GetAxisGeometry(graphPtr, axisPtr)
  1861.     Graph *graphPtr;
  1862.     Axis *axisPtr;
  1863. {
  1864.     register int i;
  1865.     register int count;
  1866.     char label[LABEL_MAX_SIZE+1];
  1867.     unsigned int length;
  1868.     unsigned int arraySize, poolSize, used;
  1869.     char *pool;
  1870.     unsigned int textWidth, textHeight;
  1871.     unsigned int bbWidth, bbHeight;
  1872.     int maxWidth, maxHeight;
  1873.     double value;
  1874.     double epsilon, minAxis, maxAxis;
  1875.     Label *labelArr;
  1876.     int pad;
  1877.  
  1878.     if ((axisPtr->logScale) || (axisPtr->loose) ||
  1879.     (axisPtr->limits[LMIN] == axisPtr->limits[LMAX])) {
  1880.     minAxis = axisPtr->tickMin, maxAxis = axisPtr->tickMax;
  1881.     } else {
  1882.     minAxis = axisPtr->limits[LMIN];
  1883.     maxAxis = axisPtr->limits[LMAX];
  1884.     }
  1885.  
  1886.     /* Use numbers just beyond the limits when testing for equality */
  1887.  
  1888.     epsilon = 2 * MIN_DBL_VALUE;
  1889.     minAxis -= epsilon, maxAxis += epsilon;
  1890.  
  1891.     /* Create an array of labels with an attached of characters strings */
  1892.     arraySize = axisPtr->numTicks * sizeof(Label);
  1893.     poolSize = axisPtr->numTicks * AVG_TICK_NUM_CHARS * sizeof(char);
  1894.     labelArr = (Label *)malloc(arraySize + poolSize);
  1895.     pool = (char *)labelArr + arraySize;
  1896.  
  1897.     textHeight = TEXTHEIGHT(axisPtr->fontPtr);
  1898.  
  1899.     maxHeight = maxWidth = 0;
  1900.     used = count = 0;
  1901.     value = axisPtr->tickMin;
  1902.     for (i = 0; i < axisPtr->numTicks; i++, value += axisPtr->step) {
  1903.     value = UROUND(value, axisPtr->step);
  1904.     if ((value < minAxis) || (value > maxAxis)) {
  1905.         continue;        /* Out of range */
  1906.     }
  1907.     MakeLabel(graphPtr, axisPtr, value, label);
  1908.     length = strlen(label);
  1909.  
  1910.     /*  Resize the label array if we overflow its string pool */
  1911.     if (poolSize <= (used + length + 1)) {
  1912.         int newSize;
  1913.         Label *newArr;
  1914.  
  1915.         newSize = poolSize + poolSize;
  1916.         while (newSize <= (used + length + 1)) {
  1917.         newSize += newSize;
  1918.         }
  1919.         newArr = (Label *)malloc(arraySize + newSize);
  1920.         memcpy((char *)newArr, (char *)labelArr, arraySize + poolSize);
  1921.         pool = (char *)newArr + arraySize;
  1922.         free((char *)labelArr);
  1923.         poolSize = newSize;
  1924.         labelArr = newArr;
  1925.     }
  1926.     textWidth = Blt_TextStringWidth(axisPtr->fontPtr, label);
  1927.     labelArr[count].text = (pool + used);
  1928.     strcpy(labelArr[count].text, label);
  1929.     used += (length + 1);
  1930.     count++;
  1931.  
  1932.     if (axisPtr->theta == 0.0) {
  1933.         bbWidth = textWidth, bbHeight = textHeight;
  1934.     } else {
  1935.         Blt_GetBoundingBox(textWidth, textHeight, axisPtr->theta,
  1936.         &bbWidth, &bbHeight, (XPoint *)NULL);
  1937.     }
  1938.     if (bbWidth > maxWidth) {
  1939.         maxWidth = bbWidth;
  1940.     }
  1941.     if (bbHeight > maxHeight) {
  1942.         maxHeight = bbHeight;
  1943.     }
  1944.     }
  1945.     if (axisPtr->labelArr != NULL) {
  1946.     free((char *)axisPtr->labelArr);
  1947.     }
  1948.     axisPtr->labelArr = labelArr;
  1949.     axisPtr->numLabels = count;
  1950.     assert(axisPtr->numLabels <= axisPtr->numTicks);
  1951.  
  1952.     /*
  1953.      * Because the axis cap style is "CapProjecting", there's an extra
  1954.      * 1.5 linewidth to be accounted for.
  1955.      */
  1956.     pad = ((axisPtr->lineWidth * 15) / 10) + 2;
  1957.     axisPtr->width = maxWidth + pad;
  1958.     axisPtr->height = maxHeight + pad;
  1959.  
  1960.     value = BLT_ABS(axisPtr->tickLength) * 1.4;
  1961.     pad = BLT_RND(value) + graphPtr->plotBW + 1;
  1962.     if (graphPtr->plotBW > 0) {
  1963.     pad++;
  1964.     }
  1965.     if (HORIZONTAL_AXIS(axisPtr)) {
  1966.     axisPtr->height += pad;
  1967.     } else {
  1968.     axisPtr->width += pad;
  1969.     }
  1970. }
  1971.  
  1972. void
  1973. Blt_UpdateAxisBackgrounds(graphPtr, colorPtr)
  1974.     Graph *graphPtr;
  1975.     XColor *colorPtr;        /* Background color of graph margin area */
  1976. {
  1977.     Axis *axisPtr;
  1978.     register int i;
  1979.  
  1980.     for (i = 0; i < 4; i++) {
  1981.     axisPtr = (Axis *)graphPtr->axisArr[i];
  1982.     XSetBackground(Tk_Display(graphPtr->tkwin), axisPtr->textGC,
  1983.         colorPtr->pixel);
  1984.     }
  1985. }
  1986.  
  1987. /*
  1988.  * -----------------------------------------------------------------
  1989.  *
  1990.  * Blt_ComputeLayout --
  1991.  *
  1992.  *     Calculate the layout of the graph.  Based upon the data,
  1993.  *    axis limits, X and Y titles, and title height, determine
  1994.  *    the cavity left which is the plotting surface.  The first
  1995.  *    step get the data and axis limits for calculating the space
  1996.  *    needed for the top, bottom, left, and right margins.
  1997.  *
  1998.  *     1) The LEFT margin is the area from the left border to the
  1999.  *       Y axis (not including ticks). It composes the border
  2000.  *       width, the width an optional Y axis label and its padding,
  2001.  *       and the tick numeric labels. The Y axis label is rotated
  2002.  *       90 degrees so that the width is the font height.
  2003.  *
  2004.  *     2) The RIGHT margin is the area from the end of the graph
  2005.  *       to the right window border. It composes the border width,
  2006.  *       some padding, the font height (this may be dubious. It
  2007.  *       appears to provide a more even border), the max of the
  2008.  *       legend width and 1/2 max X tick number. This last part is
  2009.  *       so that the last tick label is not clipped.
  2010.  *
  2011.  *           Area Width
  2012.  *      ___________________________________________________________
  2013.  *      |          |                               |               |
  2014.  *      |          |   TOP  height of title        |               |
  2015.  *      |          |                               |               |
  2016.  *      |          |           x2 title            |               |
  2017.  *      |          |                               |               |
  2018.  *      |          |        height of x2-axis      |               |
  2019.  *      |__________|_______________________________|_______________|  A
  2020.  *      |          |                        extreme|               |  r
  2021.  *      |          |                               |               |  e
  2022.  *      |   LEFT   |                               |     RIGHT     |  a
  2023.  *      |          |                               |               |
  2024.  *      | y        |     Free area = 104%          |      y2       |  H
  2025.  *      |          |     Plotting surface = 100%   |               |  e
  2026.  *      | t        |     Tick length = 2 + 2%      |      t        |  i
  2027.  *      | i        |                               |      i        |  g
  2028.  *      | t        |                               |      t  legend|  h
  2029.  *      | l        |                               |      l   width|  t
  2030.  *      | e        |                               |      e        |
  2031.  *      |    height|                               |height         |
  2032.  *      |       of |                               | of            |
  2033.  *      |    y-axis|                               |y2-axis        |
  2034.  *      |          |                               |               |
  2035.  *      |          |origin                         |               |
  2036.  *      |__________|_______________________________|_______________|
  2037.  *      |          | (xoffset, yoffset)            |               |
  2038.  *      |          |                               |               |
  2039.  *      |          |       height of x-axis        |               |
  2040.  *      |          |                               |               |
  2041.  *      |          |   BOTTOM   x title            |               |
  2042.  *      |__________|_______________________________|_______________|
  2043.  *
  2044.  * 3) The TOP margin is the area from the top window border to the top
  2045.  *    of the graph. It composes the border width, twice the height of
  2046.  *    the title font (if one is given) and some padding between the
  2047.  *    title.
  2048.  *
  2049.  * 4) The BOTTOM margin is area from the bottom window border to the
  2050.  *    X axis (not including ticks). It composes the border width, the height
  2051.  *    an optional X axis label and its padding, the height of the font
  2052.  *    of the tick labels.
  2053.  *
  2054.  * The plotting area is between the margins which includes the X and Y axes
  2055.  * including the ticks but not the tick numeric labels. The length of
  2056.  * the ticks and its padding is 5% of the entire plotting area.  Hence the
  2057.  * entire plotting area is scaled as 105% of the width and height of the
  2058.  * area.
  2059.  *
  2060.  * The axis labels, ticks labels, title, and legend may or may not be
  2061.  * displayed which must be taken into account.
  2062.  *
  2063.  *
  2064.  * -----------------------------------------------------------------
  2065.  */
  2066. int
  2067. Blt_ComputeLayout(graphPtr)
  2068.     Graph *graphPtr;
  2069. {
  2070.     int left, right, top, bottom;
  2071.     int maxTickWidth;
  2072.     int height;
  2073.     int leftOver;
  2074.     unsigned int borderWidths;
  2075.     unsigned int lineHeight = TEXTHEIGHT(graphPtr->fontPtr);
  2076.     Axis *x1, *x2, *y1, *y2;
  2077.     unsigned int twiceHeight = (2 * lineHeight);
  2078.     unsigned int halfHeight = (lineHeight / 2);
  2079.     double value;
  2080.  
  2081.     x1 = (Axis *)graphPtr->bottomAxis;
  2082.     x2 = (Axis *)graphPtr->topAxis;
  2083.     y1 = (Axis *)graphPtr->leftAxis;
  2084.     y2 = (Axis *)graphPtr->rightAxis;
  2085.  
  2086.     top = (graphPtr->title != NULL) ? twiceHeight : halfHeight;
  2087.     left = ((y1->mapped) && (y1->title != NULL)) ? twiceHeight : halfHeight;
  2088.     bottom = ((x1->mapped) && (x1->title != NULL)) ? twiceHeight : halfHeight;
  2089.     right = ((y2->mapped) && (y2->title != NULL)) ? twiceHeight : 0;
  2090.  
  2091.     if ((x2->mapped) && (x2->title != NULL)) {
  2092.     top += twiceHeight;
  2093.     }
  2094.     GetAxisGeometry(graphPtr, x1);
  2095.     maxTickWidth = 0;
  2096.     if ((x1->mapped) && (x1->showTicks)) {
  2097.     bottom += x1->height;
  2098.     }
  2099.     GetAxisGeometry(graphPtr, x2);
  2100.     if ((x2->mapped) && (x2->showTicks)) {
  2101.     top += x2->height;
  2102.     }
  2103.     GetAxisGeometry(graphPtr, y1);
  2104.     if ((y1->mapped) && (y1->showTicks)) {
  2105.     left += y1->width + PADX;
  2106.     }
  2107.     GetAxisGeometry(graphPtr, y2);
  2108.     if ((y2->mapped) && (y2->showTicks)) {
  2109.     right += y2->width + PADX;
  2110.     }
  2111.     /* Override calculated values if user specified margins */
  2112.  
  2113.     if (graphPtr->leftMargin > 0) {
  2114.     left = graphPtr->leftMargin;
  2115.     }
  2116.     if (graphPtr->topMargin > 0) {
  2117.     top = graphPtr->topMargin;
  2118.     }
  2119.     if (graphPtr->bottomMargin > 0) {
  2120.     bottom = graphPtr->bottomMargin;
  2121.     }
  2122.     borderWidths = graphPtr->borderWidth + graphPtr->plotBW;
  2123.     height = graphPtr->height - ((2 * borderWidths) + top + bottom);
  2124.     (*graphPtr->legendPtr->geomProc) (graphPtr, height);
  2125.     if ((graphPtr->legendPtr->mapped) && (graphPtr->legendPtr->useDefault)) {
  2126.     right += graphPtr->legendPtr->width;
  2127.     } else {
  2128.     right += halfHeight;
  2129.     }
  2130.     maxTickWidth = BLT_MAX(x1->width, x2->width) / 2;
  2131.     if (right < maxTickWidth) {
  2132.     right = maxTickWidth;
  2133.     }
  2134.     if (graphPtr->rightMargin > 0) {
  2135.     right = graphPtr->rightMargin;
  2136.     }
  2137.     top += borderWidths;
  2138.     left += borderWidths;
  2139.     right += borderWidths;
  2140.     bottom += borderWidths;
  2141.  
  2142.     /* Based upon the margins, calculate the space left for the graph. */
  2143.  
  2144.     x1->offset = left;
  2145.     y1->offset = graphPtr->height - bottom;
  2146.     leftOver = graphPtr->width - (left + right);
  2147.     if (leftOver < 0) {
  2148.     return TCL_ERROR;
  2149.     }
  2150.     x1->scale = (double)leftOver;    /* Pixels per X unit */
  2151.     leftOver = graphPtr->height - (top + bottom);
  2152.     if (leftOver < 0) {
  2153.     return TCL_ERROR;
  2154.     }
  2155.     y1->scale = (double)leftOver;    /* Pixels per Y unit */
  2156.     x2->scale = x1->scale;
  2157.     x2->offset = x1->offset;
  2158.     y2->scale = y1->scale;
  2159.     y2->offset = y1->offset;
  2160.  
  2161.     /* Calculate the average symbol (formula is arbitrary) */
  2162.  
  2163.     value = log(((double)x1->scale) * y1->scale) * 0.8;
  2164.     graphPtr->avgSymSize = BLT_RND(value);
  2165.  
  2166.     graphPtr->origin.x = MAPX(x1, 0.0);
  2167.     graphPtr->origin.y = MAPY(y1, 0.0);
  2168.     graphPtr->extreme.x = MAPX(x1, 1.0);
  2169.     graphPtr->extreme.y = MAPY(y1, 1.0);
  2170.     return TCL_OK;
  2171. }
  2172.  
  2173. /*
  2174.  *--------------------------------------------------------------
  2175.  *
  2176.  * GetAxisLimits --
  2177.  *
  2178.  *    This procedure returns a string representing the axis limits
  2179.  *    of the graph.  The format of the string is { xmin ymin xmax ymax}.
  2180.  *
  2181.  * Results:
  2182.  *    Always returns TCL_OK.  The interp->result field is
  2183.  *    a list of the graph axis limits.
  2184.  *
  2185.  *--------------------------------------------------------------
  2186.  */
  2187. static int
  2188. GetAxisLimits(axisPtr, argc, argv)
  2189.     Axis *axisPtr;
  2190.     int argc;
  2191.     char **argv;
  2192.  
  2193. {
  2194.     char string[TCL_DOUBLE_SPACE + 1];
  2195.     double min, max;
  2196.  
  2197.     if (argc != 3) {
  2198.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2199.         argv[0], " ", axisNames[axisPtr->type], "axis limits\"",
  2200.         (char *)NULL);
  2201.     return TCL_ERROR;
  2202.     }
  2203.     min = axisPtr->min, max = axisPtr->max;
  2204.     if (axisPtr->logScale) {
  2205.     min = BLT_EXP10(min);
  2206.     max = BLT_EXP10(max);
  2207.     }
  2208.     Tcl_PrintDouble(axisPtr->interp, min, string);
  2209.     Tcl_AppendElement(axisPtr->interp, string);
  2210.     Tcl_PrintDouble(axisPtr->interp, max, string);
  2211.     Tcl_AppendElement(axisPtr->interp, string);
  2212.     return TCL_OK;
  2213. }
  2214.  
  2215. /*
  2216.  * ----------------------------------------------------------------------
  2217.  *
  2218.  * InvTransformCoord --
  2219.  *
  2220.  *    Maps the given window coordinate into an axis-value.
  2221.  *
  2222.  * Results:
  2223.  *    Returns a standard Tcl result.  interp->result contains
  2224.  *    the axis value. If an error occurred, TCL_ERROR is returned
  2225.  *    and interp->result will contain an error message.
  2226.  *
  2227.  * ----------------------------------------------------------------------
  2228.  */
  2229. static int
  2230. InvTransformCoord(axisPtr, argc, argv)
  2231.     Axis *axisPtr;
  2232.     int argc;
  2233.     char **argv;
  2234. {
  2235.     int coord;            /* Integer window coordinate*/
  2236.     char string[TCL_DOUBLE_SPACE + 1];
  2237.     double value;
  2238.  
  2239.     if (argc != 4) {
  2240.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2241.         argv[0], " ", axisNames[axisPtr->type],
  2242.         "axis invtransform winPos\"", (char *)NULL);
  2243.     return TCL_ERROR;
  2244.     }
  2245.     if (Tcl_GetInt(axisPtr->interp, argv[2], &coord) != TCL_OK) {
  2246.     return TCL_ERROR;
  2247.     }
  2248.     value = Blt_InvTransform((GraphAxis *)axisPtr, coord);
  2249.     Tcl_PrintDouble(axisPtr->interp, value, string);
  2250.     Tcl_AppendElement(axisPtr->interp, string);
  2251.     return TCL_OK;
  2252. }
  2253.  
  2254. /*
  2255.  * ----------------------------------------------------------------------
  2256.  *
  2257.  * TransformCoord --
  2258.  *
  2259.  *    Maps the given axis-value to a window coordinate.
  2260.  *
  2261.  * Results:
  2262.  *    Returns a standard Tcl result.  interp->result contains
  2263.  *    the window coordinate. If an error occurred, TCL_ERROR
  2264.  *    is returned and interp->result will contain an error
  2265.  *    message.
  2266.  *
  2267.  * ----------------------------------------------------------------------
  2268.  */
  2269. static int
  2270. TransformCoord(axisPtr, argc, argv)
  2271.     Axis *axisPtr;        /* Axis */
  2272.     int argc;
  2273.     char **argv;
  2274. {
  2275.     double value;
  2276.     int coord;
  2277.  
  2278.     if (argc != 4) {
  2279.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2280.         argv[0], " ", axisNames[axisPtr->type], "axis transform value\"",
  2281.         (char *)NULL);
  2282.     return TCL_ERROR;
  2283.     }
  2284.     if (Tcl_ExprDouble(axisPtr->interp, argv[2], &value) != TCL_OK) {
  2285.     return TCL_ERROR;
  2286.     }
  2287.     coord = Blt_Transform((GraphAxis *)axisPtr, value);
  2288.     sprintf(axisPtr->interp->result, "%d", coord);
  2289.     return TCL_OK;
  2290. }
  2291.  
  2292. /*
  2293.  * ----------------------------------------------------------------------
  2294.  *
  2295.  * Blt_CreateAxis --
  2296.  *
  2297.  *    Create and initialize a structure containing information to
  2298.  *     display a graph axis.
  2299.  *
  2300.  * Results:
  2301.  *    The return value is a standard Tcl result.
  2302.  *
  2303.  * ----------------------------------------------------------------------
  2304.  */
  2305. int
  2306. Blt_CreateAxis(graphPtr, type, flags)
  2307.     Graph *graphPtr;
  2308.     enum AxisTypes type;
  2309.     int flags;            /* Configuration flags */
  2310. {
  2311.     Axis *axisPtr;
  2312.     enum AxisLocations location;
  2313.  
  2314.     axisPtr = (Axis *)calloc(1, sizeof(Axis));
  2315.     if (axisPtr == NULL) {
  2316.     graphPtr->interp->result = "can't allocate axis structure";
  2317.     return TCL_ERROR;
  2318.     }
  2319.     location = (AxisLocation) type;    /* For now, there's a 1-1
  2320.                      * correspondence between axis
  2321.                      * types and locations */
  2322.     axisPtr->type = type;
  2323.     axisPtr->location = location;
  2324.     axisPtr->interp = graphPtr->interp;    /* Needed for Tcl_PrintDouble */
  2325.     axisPtr->showTicks = 1;
  2326.     axisPtr->reqSubTicks = 2;
  2327.     axisPtr->destroyProc = DestroyAxis;
  2328.     axisPtr->displayProc = DisplayAxis;
  2329.     axisPtr->layoutProc = LayoutAxis;
  2330.     axisPtr->printProc = PrintAxis;
  2331.  
  2332.     /*
  2333.      * The actual axis min and max can't be the same, so initializing
  2334.      * the previous limits to zero is Ok.
  2335.      */
  2336.     axisPtr->prevMin = axisPtr->prevMax = 0.0;
  2337.     axisPtr->mapped = ((location == BOTTOM_AXIS) || (location == LEFT_AXIS));
  2338.  
  2339.     graphPtr->axisArr[type] = (GraphAxis *)axisPtr;
  2340.     if (ConfigureAxis(graphPtr, axisPtr, 0, (char **)NULL, flags) != TCL_OK) {
  2341.     return TCL_ERROR;
  2342.     }
  2343.     return TCL_OK;
  2344. }
  2345.  
  2346. int
  2347. Blt_AxisCmd(graphPtr, axis, argc, argv, flags)
  2348.     Graph *graphPtr;
  2349.     GraphAxis *axis;
  2350.     int argc;
  2351.     char **argv;
  2352.     int flags;
  2353. {
  2354.     int result = TCL_ERROR;
  2355.     Axis *axisPtr = (Axis *)axis;
  2356.     Tcl_Interp *interp = graphPtr->interp;
  2357.     char c;
  2358.     int length;
  2359.     char *which;
  2360.  
  2361.     which = axisNames[axisPtr->type];
  2362.     if (argc < 3) {
  2363.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2364.         " ", which, "axis option ?args?\"", NULL);
  2365.     return TCL_ERROR;
  2366.     }
  2367.     c = argv[2][0];
  2368.     length = strlen(argv[2]);
  2369.  
  2370.     if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)) {
  2371.     if (argc < 3) {
  2372.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2373.         " ", which, "axis configure ?args?\"", NULL);
  2374.         return TCL_ERROR;
  2375.     }
  2376.     result = ConfigureAxis(graphPtr, axisPtr, argc - 3, argv + 3, flags);
  2377.     } else if ((c == 'l') && (strncmp(argv[2], "limits", length) == 0)) {
  2378.     result = GetAxisLimits(axisPtr, argc, argv);
  2379.     } else if ((c == 'i') && (strncmp(argv[2], "invtransform", length) == 0)) {
  2380.     result = InvTransformCoord(axisPtr, argc, argv);
  2381.     } else if ((c == 't') && (strncmp(argv[2], "transform", length) == 0)) {
  2382.     result = TransformCoord(axisPtr, argc, argv);
  2383.     } else {
  2384.     Tcl_AppendResult(interp, "bad ", which, "axis option \"", argv[2],
  2385.         "\":  should be configure or limits", (char *)NULL);
  2386.     return TCL_ERROR;
  2387.     }
  2388.     return result;
  2389. }
  2390.